{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 层次分析法案例实现"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1. 层次分析法定义\n",
    "层次分析法是 多目标决策问题 的一个解决方案。简称 AHP。它把有关的元素分解成目标、准则、方案等层次，在此基础之上进行定性和定量分析的决策方法。该方法是美国运筹学家匹茨堡大学教授萨蒂于20世纪70年代初提出的。人们分析问题时，经常面对一个由相互关联、相互制约的众多因素构成的复杂系统。层次分析法则为研究这类复杂的系统，提供了一种新的、简洁的、实用的决策方法。\n",
    "#### 2. 层次分析法结构\n",
    "- 完全相关性结构：上层每一因素与下层所有因素均有联系\n",
    "- 完全独立性结构：上层每一因素都有独立的下层要素\n",
    "- 混合型结构：上述两种结构的混合结构\n",
    "\n",
    "#### 3.层次分析法流程\n",
    "- 1.建立递阶层次结构模型；\n",
    "- 2.构造出各层次中的所有判断矩阵；\n",
    "- 3.层次单排序及一致性检验；\n",
    "- 4.层次总排序及一致性检验。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 分析案例\n",
    "有m=3个候选干部，n=5个评价指标，分别是品德、才能、资历、年龄、群众关系。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import warnings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.1 准则层重要性矩阵\n",
    "重要性：1-9,1 表示相同，9 表示绝对强。下列数据中的 7 表示品德对比资历的重要度为 7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "criteria = np.array([[1, 2, 7, 5, 5],\n",
    "                     [1 / 2, 1, 4, 3, 3],\n",
    "                     [1 / 7, 1 / 4, 1, 1 / 2, 1 / 3],\n",
    "                     [1 / 5, 1 / 3, 2, 1, 1],\n",
    "                     [1 / 5, 1 / 3, 3, 1, 1]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.2 方案层各个准则对应的判断矩阵\n",
    "针对每一个评价指标对于不同的方案都有不同的打分，1 表示相同，大于 1 表示优于另一个方案，小于 1 表示差与另一个方案。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "b1 = np.array([[1, 1 / 3, 1 / 8], [3, 1, 1 / 3], [8, 3, 1]])\n",
    "b2 = np.array([[1, 2, 5], [1 / 2, 1, 2], [1 / 5, 1 / 2, 1]])\n",
    "b3 = np.array([[1, 1, 3], [1, 1, 3], [1 / 3, 1 / 3, 1]])\n",
    "b4 = np.array([[1, 3, 4], [1 / 3, 1, 1], [1 / 4, 1, 1]])\n",
    "b5 = np.array([[1, 4, 1 / 2], [1 / 4, 1, 1 / 4], [2, 4, 1]])\n",
    "\n",
    "b = [b1, b2, b3, b4, b5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.3 准则层权重计算\n",
    "返回：最大特征值、一致性检验系数以及准则层的参数列表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cal_weights(input_matrix):\n",
    "    # RI指标表，计算的一致性系数CR小于0.1，则表示有较强的一致性\n",
    "    RI = (0, 0, 0.58, 0.9, 1.12, 1.24, 1.32, 1.41, 1.45, 1.49)\n",
    "    n1, n2 = input_matrix.shape\n",
    "    assert n1 == n2, '不是一个方阵'\n",
    "    for i in range(n1):\n",
    "        for j in range(n2):\n",
    "            if np.abs(input_matrix[i, j] * input_matrix[j, i] - 1) > 1e-7:\n",
    "                raise ValueError('不是反对称矩阵')\n",
    "    # 计算奇异值方阵，返回特征值和特征向量\n",
    "    eig_vals, eig_vectors = np.linalg.eig(input_matrix)\n",
    "    # 返回行数最大值的索引\n",
    "    max_idx = np.argmax(eig_vals)\n",
    "    # 获取最大特征值\n",
    "    max_val = eig_vals[max_idx].real\n",
    "    # 获取最大特征值对应的特征列向量\n",
    "    max_vector = eig_vectors[:, max_idx].real\n",
    "    # 数据归一化\n",
    "    max_vector = max_vector / max_vector.sum()\n",
    "    \n",
    "    if n1 > 9:\n",
    "        CR = None\n",
    "        warnings.warn('无法判断一致性！')\n",
    "    else:\n",
    "        CI = (max_val - n1) / (n1 - 1)\n",
    "        CR = CI / RI[n1]\n",
    "    return max_val, CR, max_vector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "准则层：最大特征值5.072084,CR=0.014533,检验通过\n",
      "准则层权重=[0.47583538 0.26360349 0.0538146  0.09806829 0.10867824]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "max_val, CR, max_vector = cal_weights(criteria)\n",
    "print('准则层：最大特征值{:<5f},CR={:<5f},检验{}通过'.format(max_val, CR, '' if CR < 0.1 else '不'))\n",
    "print('准则层权重={}\\n'.format(max_vector))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.4 方案层权重计算\n",
    "返回每个判断矩阵的：最大特征值、一致性检验系数以及准则层的参数列表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "方案层\n",
      "          方案0       方案1       方案2     最大特征值            CR  一致性检验\n",
      "准则0  0.081935  0.236341  0.681725  3.001542  8.564584e-04   True\n",
      "准则1  0.595379  0.276350  0.128271  3.005535  3.075062e-03   True\n",
      "准则2  0.428571  0.428571  0.142857  3.000000 -1.233581e-15   True\n",
      "准则3  0.633708  0.191921  0.174371  3.009203  5.112618e-03   True\n",
      "准则4  0.344545  0.108525  0.546931  3.053622  2.978976e-02   True\n"
     ]
    }
   ],
   "source": [
    "max_eig_list, CR_list, eig_list = [], [], []\n",
    "for i in b:\n",
    "    max_eig, CR, eig = cal_weights(i)\n",
    "    max_eig_list.append(max_eig)\n",
    "    CR_list.append(CR)\n",
    "    eig_list.append(eig)\n",
    "    \n",
    "pd_print = pd.DataFrame(eig_list,\n",
    "                        index=['准则' + str(i) for i in range(5)],\n",
    "                        columns=['方案' + str(i) for i in range(3)],\n",
    "                        )\n",
    "pd_print.loc[:, '最大特征值'] = max_eig_list\n",
    "pd_print.loc[:, 'CR'] = CR_list\n",
    "pd_print.loc[:, '一致性检验'] = pd_print.loc[:, 'CR'] < 0.1\n",
    "print('方案层')\n",
    "print(pd_print)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1.5 目标层方案计算\n",
    "将准则层计算得到的最佳特征向量与方案层计算得到特征向量矩阵做点乘"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "目标层： [[0.318586   0.23898522 0.44242878]]\n",
      "最优选择方案2\n"
     ]
    }
   ],
   "source": [
    "obj = np.dot(max_vector.reshape(1, -1), np.array(eig_list))\n",
    "print('\\n目标层：', obj)\n",
    "print('最优选择方案{}'.format(np.argmax(obj)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "### "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
